import requests
import json
import re
from urllib.parse import quote
import time
import base64

# 配置
JSON_PATH = r"D:\AI\ComfyUI\custom_nodes\ComfyUI-Manager\custom-node-list.json"
username = "用户名"
password = "密码"

# 登录部分（完全保留原脚本逻辑）
encoded_password = "8OaI64xWuGi9LUKQfopc3A%3D%3D"
post_data = f"username={quote(username)}&password={encoded_password}"
headers = {
    "Accept": "application/json, text/plain, */*",
    "Content-Type": "application/x-www-form-urlencoded",
    "Referer": "https://gitcode.com/",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
    "X-App-Channel": "gitcode-fe",
    "X-App-Version": "0",
    "X-Device-ID": "unknown",
    "X-Device-Type": "Windows",
    "X-Network-Type": "4g",
    "X-OS-Version": "10",
    "X-Platform": "web",
    "X-Request-ID": "unknown",
    "X-User-ID": "unknown"
}
session = requests.Session()
session.headers.update(headers)
print("🔑 正在登录 GitCode...")
response = session.post("https://web-api.gitcode.com/uc/api/v1/user/oauth/login", data=post_data)
print(f"状态码: {response.status_code}")
print(f"响应内容: {response.text}")
if response.status_code == 200:
    login_data = response.json()
    print("✅ 登录成功!")
    print(f"用户名: {login_data.get('username')}")
    print(f"邮箱: {login_data.get('email')}")
    print(f"用户ID: {login_data.get('id')}")
    access_token = login_data.get('access_token')
    username = login_data.get('username')
    if not access_token:
        print("❌ 未获取到access_token")
        exit(1)
else:
    print("❌ 登录失败!", response.text)
    exit(1)

def extract_repo_name_from_github_url(github_url):
    patterns = [
        r'github\.com/([^/]+)/([^/]+?)(?:\.git)?/?$',
        r'github\.com/([^/]+)/([^/]+)/?$',
    ]
    for pattern in patterns:
        match = re.search(pattern, github_url)
        if match:
            owner, repo_name = match.groups()
            return repo_name
    return None

def get_project_id_by_name(session, access_token, username, repo_name):
    """通过仓库名称获取项目ID"""
    print(f"🔍 查找已存在的仓库: {repo_name}")
    search_url = f"https://web-api.gitcode.com/api/v1/projects?search={repo_name}&membership=true"
    headers = {
        "Accept": "application/json, text/plain, */*",
        "Authorization": f"Bearer {access_token}",
        "Referer": "https://gitcode.com/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
    }
    
    response = session.get(search_url, headers=headers)
    print(f"搜索仓库状态码: {response.status_code}")
    
    if response.status_code == 200:
        try:
            projects_data = response.json()
            
            # 根据实际API结构处理数据
            if isinstance(projects_data, dict) and 'content' in projects_data:
                projects = projects_data['content']
                print(f"找到 {len(projects)} 个仓库")
                
                for project in projects:
                    if isinstance(project, dict):
                        project_name = project.get('name')
                        namespace_info = project.get('namespace_info', {})
                        owner_username = namespace_info.get('name') if isinstance(namespace_info, dict) else None
                        
                        print(f"检查仓库: {project_name}, 所有者: {owner_username}")
                        
                        if project_name == repo_name and owner_username == username:
                            project_id = project.get('id')
                            print(f"✅ 找到已存在的仓库，项目ID: {project_id}")
                            return project_id
            else:
                print(f"⚠️ 意外的API数据结构: {type(projects_data)}")
                
        except Exception as e:
            print(f"❌ JSON解析失败: {e}")
    else:
        print(f"❌ 搜索仓库失败: {response.text}")
    
    print(f"❌ 未找到已存在的仓库: {repo_name}")
    return None

def create_new_repo_with_mirror(session, access_token, username, github_url):
    repo_name = extract_repo_name_from_github_url(github_url)
    if repo_name:
        print(f"📦 提取到仓库名称: {repo_name}")
        # 先查找仓库是否已存在
        project_id = get_project_id_by_name(session, access_token, username, repo_name)
        if project_id:
            print("⚠️ 仓库已存在，跳过创建...")

            mirror_result = setup_mirror_for_repo(session, access_token, username, repo_name, github_url)
            return "success" if mirror_result else False
            #return "installed"
        # 不存在则创建
        create_repo_headers = {
            "Accept": "application/json, text/plain, */*",
            "Content-Type": "application/json",
            "Authorization": f"Bearer {access_token}",
            "Referer": "https://gitcode.com/",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
        }
        repo_data = {
            "name": repo_name,
            "description": f"Mirror of {github_url}。 comfyui在中国，镜像加速。版权归原作者所有",
            "visibility": "public",
            "initialize_with_readme": True
        }
        create_response = session.post(
            "https://web-api.gitcode.com/api/v1/projects",
            headers=create_repo_headers,
            json=repo_data
        )
        print(f"创建仓库状态码: {create_response.status_code}")
        if create_response.status_code in [200, 201]:
            print("🎉 仓库创建成功!")
            mirror_result = setup_mirror_for_repo(session, access_token, username, repo_name, github_url)
            return "success" if mirror_result else False
        elif create_response.status_code == 422:
            print("⚠️ 仓库已存在（并发冲突），跳过镜像设置...")
            return "installed"
        else:
            print(f"❌ 创建仓库失败: {create_response.text}")
            return False
    else:
        print("❌ 无法从GitHub URL中提取仓库名称")
        return False

def setup_mirror_for_repo(session, access_token, username, repo_name, github_url):
    print(f"\n🔄 开始设置镜像仓库...")
    repo_path = f"{username}%2F{repo_name}"
    mirror_url = f"https://web-api.gitcode.com/api/v2/projects/{repo_path}/repo_remote_mirror"
    mirror_headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {access_token}",
        "Referer": "https://gitcode.com/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
        "X-App-Channel": "gitcode-fe",
        "X-App-Version": "0",
        "X-Device-ID": "unknown",
        "X-Device-Type": "Windows",
        "X-Network-Type": "4g",
        "X-OS-Version": "10",
        "X-Platform": "web"
    }
    cookie_str = "; ".join([f"{k}={v}" for k, v in session.cookies.get_dict().items()])
    mirror_headers["Cookie"] = cookie_str
    
    # 先获取镜像信息
    print(f"🔍 获取当前镜像信息...")
    mirror_get_response = session.get(
        mirror_url,
        headers=mirror_headers
    )
    print(f"获取镜像信息响应: ",mirror_get_response)
    print(f"获取镜像信息状态码: {mirror_get_response.status_code}")
    
    if mirror_get_response.status_code == 200:
        mirror_info = mirror_get_response.json()
        print("✅ 获取镜像信息成功!")
        print(f"当前镜像URL: {mirror_info.get('url')}")
        print(f"当前镜像状态: {'启用' if mirror_info.get('mirroring_enabled') else '禁用'}")
        
        # 检查镜像是否已经正确配置
        if mirror_info.get('mirroring_enabled') and mirror_info.get('url'):
            print("✅ 镜像已正确配置，跳过设置")
            return True
        
        # 检查是否需要配置镜像源
        if not mirror_info.get('url') or not mirror_info.get('enabled'):
            print(f"\n🔄 需要配置镜像源...")
            
            # 确保GitHub URL格式正确
            if github_url.endswith('.git'):
                mirror_url_clean = github_url
            else:
                mirror_url_clean = github_url + '.git'

            encoded_url = base64.b64encode(mirror_url_clean.encode()).decode()

            mirror_update_data = {
                "url": encoded_url,
                "force": True
            }
            
            print(f"更新镜像配置: {mirror_url}")
            print(f"镜像数据: {json.dumps(mirror_update_data, indent=2, ensure_ascii=False)}")
            
            mirror_update_response = session.post(
                mirror_url,
                headers=mirror_headers,
                json=mirror_update_data
            )
            
            print(f"更新镜像配置状态码: {mirror_update_response.status_code}")
            print(f"更新镜像配置响应: {mirror_update_response.text}")
            
            if mirror_update_response.status_code == 200:
                print("✅ 镜像配置更新成功!")
            else:
                print(f"⚠️ 镜像配置更新失败: {mirror_update_response.text}")
                print("⚠️ 需要手动在GitCode界面设置镜像")
                return False
        else:
            print("✅ 镜像源已正确配置!")
        
        # 同步镜像仓库
        print(f"\n🔄 开始同步镜像仓库...")
        
        sync_url = f"https://web-api.gitcode.com/api/v2/projects/{repo_path}/sync_remote_mirror"
        
        sync_response = session.post(
            sync_url,
            headers=mirror_headers,
            json={}  # 空JSON对象
        )
        
        print(f"同步状态码: {sync_response.status_code}")
        print(f"同步响应: {sync_response.text}")
        
        if sync_response.status_code == 200:
            print("✅ 镜像同步成功!")
            print(f"🎉 镜像设置完成! 仓库 {repo_name} 已成功设置镜像并同步")
            return True
        else:
            print(f"⚠️ 镜像同步失败: {sync_response.text}")
            return False
    else:
        print(f"❌ 获取镜像信息失败: {mirror_get_response.text}")
        print("⚠️ 可能仓库不存在或没有镜像权限")
        return False

# 读取 custom_nodes 并批量迁移
def batch_mirror():
    with open(JSON_PATH, 'r', encoding='utf-8') as f:
        data = json.load(f)
        node_list = data.get('custom_nodes', [])
    repo_urls = []
    for item in node_list:
        url = item.get('reference')
        if url and 'github.com' in url:
            repo_urls.append(url)
    # 去重并倒序处理
    repo_urls = list(dict.fromkeys(repo_urls))  # 保持顺序的去重
    repo_urls.reverse()  # 倒序
    print(f"共发现 {len(repo_urls)} 个github仓库")
    print("🔄 按倒序处理仓库列表...")
    
    # 进度跟踪变量
    start_time = time.time()
    success_count = 0
    failed_count = 0
    installed_count = 0  # 已安装的仓库数量
    
    for i, github_url in enumerate(repo_urls, 1):
        # 计算进度
        progress = (i - 1) / len(repo_urls) * 100
        elapsed_time = time.time() - start_time
        
        # 计算预估剩余时间
        if i > 1:
            avg_time_per_repo = elapsed_time / (i - 1)
            remaining_repos = len(repo_urls) - i + 1
            estimated_remaining = avg_time_per_repo * remaining_repos
            eta_str = f"预计剩余: {estimated_remaining:.1f}秒"
        else:
            eta_str = "预计剩余: 计算中..."
        
        # 显示进度条
        progress_bar_length = 30
        filled_length = int(progress_bar_length * progress / 100)
        progress_bar = "█" * filled_length + "░" * (progress_bar_length - filled_length)
        
        print(f"\n{'='*60}")
        print(f"📊 进度: [{i}/{len(repo_urls)}] {progress:.1f}%")
        print(f"⏱️  已用时间: {elapsed_time:.1f}秒 | {eta_str}")
        print(f"✅ 成功: {success_count} | 🔄 已安装: {installed_count} | ❌ 失败: {failed_count}")
        print(f"📈 [{progress_bar}] {progress:.1f}%")
        print(f"🔄 正在处理: {github_url}")
        print(f"{'='*60}")
        
        # 处理仓库
        result = create_new_repo_with_mirror(session, access_token, username, github_url)
        if result == "success":
            success_count += 1
        elif result == "installed":
            installed_count += 1
        else:
            failed_count += 1
    
    # 最终统计
    total_time = time.time() - start_time
    print(f"\n{'='*60}")
    print(f"🎉 批量迁移完成!")
    print(f"📊 总计: {len(repo_urls)} 个仓库")
    print(f"✅ 成功: {success_count} 个")
    print(f"🔄 已安装: {installed_count} 个")
    print(f"❌ 失败: {failed_count} 个")
    print(f"⏱️  总用时: {total_time:.1f}秒")
    print(f"📈 成功率: {(success_count + installed_count)/len(repo_urls)*100:.1f}%")
    print(f"{'='*60}")

if __name__ == "__main__":
    batch_mirror() 